﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Polar.AI.Common;

namespace Polar.AI.Analysis
{
    public class CollisionAnalysis
    {
        #region Class Definition...
        public bool Collision { get; private set; }
        public Vector2 IntersectPoint { get; private set; }
        public float IntersectDistance { get; private set; }
        public float SimulationRoundsToCollision { get; private set; }

        public CollisionAnalysis()
        {
            Collision = false;
            IntersectPoint = Vector2.Zero;
            IntersectDistance = 0;
            SimulationRoundsToCollision = 0;
        }
        #endregion

        #region Static Methods...
        /// <summary>
        /// Simulates forward motion by rounds and checks for a collision.
        /// </summary>
        public static CollisionAnalysis SimulateCollision(ThreatAnalysis analysis, int simulationRounds)
        {

            float radiusSquared = Calc.Square(analysis.Source.Radius + analysis.Target.Radius);

            if (analysis.SeparationVector.LengthSquared() <= radiusSquared) //already touching
            {
                return new CollisionAnalysis()
                {
                    Collision = true,
                    IntersectDistance = 0,
                    IntersectPoint = analysis.Source.Position,
                    SimulationRoundsToCollision = 1
                };
            }

            var p1 = analysis.Source.Position;
            var p2 = analysis.Target.Position;
            Vector2 v1; Vector2 v2;
            float scale = ScaleVelocityToBodySize(analysis, out v1, out v2);

            for (int i = 0; i < simulationRounds / scale; i++)
            {
                p1 += v1;
                p2 += v2;

                if ((p2 - p1).LengthSquared() <= radiusSquared)
                {
                    return new CollisionAnalysis()
                    {
                        Collision = true,
                        IntersectDistance = (p1 - analysis.Source.Position).Length(),
                        IntersectPoint = p1,
                        SimulationRoundsToCollision = i * scale
                    };
                }
            }

            return new CollisionAnalysis();
        }

        /// <summary>
        /// Scales a velocity based on the radius sum of the two bodies.  Used for collision accuracy when simulating.
        /// </summary>
        public static float ScaleVelocityToBodySize(ThreatAnalysis analysis, out Vector2 v1, out Vector2 v2)
        {
            //if any vector is > our radius then we might jump past the object
            float radius = analysis.Source.Radius + analysis.Target.Radius;
            v1 = analysis.Source.LinearVelocity;
            v2 = analysis.Target.LinearVelocity;
            float maxVector = 0.0f;
            float scale = 1.0f;

            switch (analysis.CollisionOrientation)
            {
                case EnumCollisionOrientation.HeadOn:
                    maxVector = new[] { Math.Abs(v1.X) + Math.Abs(v2.X), Math.Abs(v1.Y) + Math.Abs(v2.Y) }.Max();
                    scale = ApplyScale(ref v1, ref v2, maxVector, radius);
                    break;
                case EnumCollisionOrientation.Overtaking:
                case EnumCollisionOrientation.PerpendicularMajor:
                case EnumCollisionOrientation.PerpendicularMinor:
                    maxVector = new[] { Math.Abs(v1.X) - Math.Abs(v2.X), Math.Abs(v1.Y) - Math.Abs(v2.Y) }.Max();
                    scale = ApplyScale(ref v1, ref v2, maxVector, radius);
                    break;
                default:
                    throw new NotSupportedException(string.Format("The following enum is not supported in ScaleVelocityToBodySize: {0}", analysis.CollisionOrientation));
            }

            return scale;
        }

        private static float ApplyScale(ref Vector2 v1, ref Vector2 v2, float maxVector, float radius)
        {
            float scale = 1.0f;
            if (maxVector > radius)
            {
                scale = radius/maxVector;
                v1 = v1*(scale);
                v2 = v2*(scale);
            }
            return scale;
        }

        #endregion
    }


}
